package yartiss.engine.simulation.system;

import java.util.Comparator;
import java.util.PriorityQueue;

import yartiss.engine.simulation.IEnergyConsumptionProfile;
import yartiss.engine.simulation.energy.profiles.consumption.ConsumptionProfileFactory;
import yartiss.engine.simulation.system.schedulable.Job;

/**
 * 
 * @author Younes Chandarli
 * 
 */
public abstract class AbstractTask implements ITask {
	private int id;
	private long firstRelease;
	private final long wcet;
	private final long wcee;
	private final long period;
	private final long deadline;
	private int priority;
	

	private Job lastExecutedJob;
	private static int count;

	public AbstractTask(long firstRelease, long wcet, long wcee, long period,	long deadline, int priority, IEnergyConsumptionProfile p) {
		if ((wcee / wcet) * wcet != wcee)
			throw new IllegalArgumentException(
					"wcee must be divisible by wcet : " + wcee + "/" + wcet);
		this.firstRelease = firstRelease;
		this.wcet = wcet;
		this.wcee = wcee;
		this.period = period;
		this.deadline = deadline;
		this.priority = priority;
		this.consumptionProfile = p;
		this.id = ++count;
	}

	@Override
	public int getId() {
		return id;
	}

	@Override
	public void setId(int id) {
		this.id = id;
	}

	@Override
	public long getFirstRelease() {
		return firstRelease;
	}
	@Override
	public void setFirstRelease(long firstRelease){
		this.firstRelease = firstRelease;
	}
	
	@Override
	public long getWcet() {
		return wcet;
	}

	@Override
	public long getWcee() {
		return wcee;
	}

	@Override
	public long getPeriod() {
		return period;
	}

	@Override
	public long getDeadline() {
		return deadline;
	}

	@Override
	public int getPriority() {
		return priority;
	}

	@Override
	public void setPriority(int priority) {
		this.priority = priority;
	}

	@Override
	public long getEnergyConsumptionRate() {
		return wcee / wcet;
	}

	@Override
	public long getEnergyConsumption(long time) {
		return consumptionProfile.getConsumed(wcet, wcee, getRemainingCost(), time);
	}

	private final PriorityQueue<Job> activeJobs = new PriorityQueue<Job>(2,
			new Comparator<Job>() {
				@Override
				public int compare(Job j1, Job j2) {
					return (int) (j1.getJobId() - j2.getJobId());
				}
			});

	@Override
	public PriorityQueue<Job> getActiveJobs() {
		return activeJobs;
	}

	@Override
	public boolean isActive() {
		return !getActiveJobs().isEmpty();
	}

	@Override
	public void activate(long time) {
		getActiveJobs().add(new Job(time, wcet, wcee, time + deadline));
	}

	@Override
	public Job getCurrentJob() {
		return getActiveJobs().peek();
	}

	@Override
	public boolean checkDeadlineMissed(long time) {
		Job job = getCurrentJob();
		if (job == null)
			return false;
		if (job.getRomainingTimeCost() > 0 && time >= job.getAbsoluteDeadline())
			return true;

		return false;
	}

	@Override
	public boolean lastExecutedJobHasCompleted() {
		return getLastExecutedJob() != getActiveJobs().peek();

	}

	@Override
	public long getRemainingCost() {
		if (getCurrentJob() != null)
			return getCurrentJob().getRomainingTimeCost();
		return 0;
	}

	@Override
	public long getNextDeadline(long time, boolean nextPeriod) {
		if (time < firstRelease)
			return firstRelease + deadline;

		long currentPeriod = (time - firstRelease) / period;

		if (nextPeriod && getCurrentJob() != null
				&& getCurrentJob().getRomainingTimeCost() == 0)
			currentPeriod++;

		return firstRelease + currentPeriod * period + deadline;
	}

	@Override
	public long getNextActivation(long time) {
		if (time < firstRelease)
			return firstRelease;
		return firstRelease + (time / period) * period + period;
	}

	@Override
	public boolean isActivationTime(long time) {
		if (time < firstRelease)
			return false;
		return (time - firstRelease) % period == 0;
	}

	@Override
	public long getNextAbsoluteDeadline(long time) {
		long currentPeriod = (time - firstRelease) / period;
		return firstRelease + currentPeriod * period + deadline;
	}

	@Override
	public long getPreviousAbsoluteDeadline(long time) {
		long currentPeriod = time / period;
		long t;
		do {
			if (currentPeriod < 0)
				return -1;
			t = firstRelease + currentPeriod * period + deadline;
			currentPeriod--;
		} while (t >= time);

		return t;
	}

	@Override
	public Job getLastExecutedJob() {
		return lastExecutedJob;

	}

	@Override
	public void setLastExecutedJob(Job lastExecutedJob) {
		this.lastExecutedJob = lastExecutedJob;

	}
	
	protected IEnergyConsumptionProfile consumptionProfile = ConsumptionProfileFactory.newIntance("default");
	@Override
	public IEnergyConsumptionProfile getEnergyConsumptionProfile() {
		return consumptionProfile;
	}
	
	protected void setEnergyConsumptionProfile(IEnergyConsumptionProfile p){
		consumptionProfile = p;
	}
	
	public abstract String getType();
}
